#!/usr/bin/perl
#
# Copyright (c) 2020, 2022, 2023 NVI, Inc.
#
# This file is part of VLBI Field System
# (see http://github.com/nvi-inc/fs).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

# key to the functioning of this program is that edited out data
# is replaced with $$$, plotting puts out of range values on the top line
# all actual values are inside (not on) box
#
# if the log values are out of time order, i.e., time was reset back,
# plots may have odd appearances, but still reflect the log

sub nrml {
#
# normalize the pcal in $i-th channel by the tsys value that matches if any
#
    my($k,$i,$chan)= @_;
    my($pv,$pt,$tv,$tt);
    my($ttref,$tvref,$ptref,$pvref);
    my($j,$min,$nfirst);
    $#pv=-1;
    $#pt=-1;
    $#tv=-1;
    $#tt=-1;
    for($j=0;$j<$pcaln[$k][$i];$j++) {
	$pv[$j]=$pcala[$k][$i][$j];
	$pt[$j]=$pcalt[$k][$i][$j];
    }
    for($j=0;$j<$tsysn{$chan};$j++) {
	$tv[$j]=$tsysv{$chan}[$j];
	$tt[$j]=$tsyst{$chan}[$j];
    }
    ($ttref,$tvref,$ptref,$pvref)=match(\@tt,\@tv,\@pt,\@pv);
#
# find normalization value
#
    $min=1;
    $nfirst=0;
    for ($j=0;$j<=$#$ttref;$j++) {
	if((!($$tvref[$j]=~/^$float_pattern$/)) || 
	   (!($$pvref[$j]=~/^$float_pattern$/))) {
	    $$pvref[$j]='$$$';
	} else {
	    $$pvref[$j]=$$pvref[$j]*sqrt($$tvref[$j]);
	    if(!$nfirst && $$tvref[$j]>0) {
		$min=$$tvref[$j];
		$nfirst=1;
	    } else {
		$min=$$tvref[$j] if $$tvref[$j]<$min && $$tvref[$j]>0;
	    }
	}
    }
    $min = $opt_k if defined($opt_k);
#
# normalize, mark bad values
#	
    for ($j=0;$j<=$#$ttref;$j++) {
	if((!($$tvref[$j]=~/^$float_pattern$/)) || 
	   (!($$pvref[$j]=~/^$float_pattern$/))) {
	    $$pvref[$j]='$$$';
	} else {
	    $$pvref[$j]=$$pvref[$j]/sqrt($min);
	}
    }
#
# be sure to return the amp time, so times will match phase times for 'p' plots
#
    return($ptref,$pvref);
}

sub match {
#
# find nearest time match for two sets of values
# $time_dist sets the limit on successful matches
# each value can match only once
# 
# if the log values are out of time order, i.e., time was reset back,
# this will only find time values nearby in log, probably what you want
#
# bad values are preserved
#
    my ($t1,$v1,$t2,$v2) = @_;
    my ($ot1,$ov1,$ot2,$ov2);
    my ($dist,$dist1,$dist2,$n1,$n2,$on);
    $n2=0;
    $on=0;
    $#ot1=-1;
    $#ov1=-1;
    $#ot2=-1;
    $#ov2=-1;
  LOOP1:
    for ($n1=0;$n1<=$#$t1;$n1++) {
      while($n2<=$#$t2) {
	  $dist=abs($$t2[$n2] - $$t1[$n1]);
	  if($n1 < $#$t1) {
	      $dist1=abs($$t2[$n2] - $$t1[$n1+1]);
	      next LOOP1 if $dist1<$dist;
	  }
	  while($n2<$#$t2) {
	      $dist2=abs($$t2[$n2+1] - $$t1[$n1]);
	      last if $dist2 > $dist;
	      $n2++;
	      $dist=$dist2;
	  }
	  last;
      }
      if($n2 <= $#$t2) {
	  if($time_dist > ($$t2[$n2] - $$t1[$n1])) {
	      $ot1[$on]=$$t1[$n1];
	      $ov1[$on]=$$v1[$n1];
	      $ot2[$on]=$$t2[$n2];
	      $ov2[$on]=$$v2[$n2];
	      $on++;
	      $n2++;
	  }
      } else {
	  last;
      }
  }

    return (\@ot1,\@ov1,\@ot2,\@ov2);
}

sub phsr {
#
# plot phase differences for RDBEs
#
    my ($tone1,$tone2)=@_;
    my ($tim1,$phs1,$tim2,$phs2);
    my ($tref1,$pref1,$tref2,$pref2,$k,$label);
    $#tim1=-1;
    $#phs1=-1;
    $#tim2=-1;
    $#phs2=-1;
    for($k=0; $k<$rpcaln{$tone1};$k++) {
	$tim1[$k]=$rpcalt{$tone1}[$k];
	$phs1[$k]=$rpcalp{$tone1}[$k];
    }
    for($k=0; $k<$rpcaln{$tone2};$k++) {
	$tim2[$k]=$rpcalt{$tone2}[$k];
	$phs2[$k]=$rpcalp{$tone2}[$k];
    }
    ($tref1,$pref1,$tref2,$pref2)=&match(\@tim1,\@phs1,\@tim2,\@phs2);

    for($k=0; $k<=$#$tref1;$k++) {
	if($$pref1[$k]!~/^$float_pattern$/ ||
	   $$pref2[$k]!~/^$float_pattern$/) {
	    $$pref1[$k]='$$$';
	} else {
	    $$pref1[$k]=$$pref2[$k]-$$pref1[$k];
	    if($$pref1[$k] > 180) {
		$$pref1[$k]-=360;
	    } elsif($$pref1[$k] < -180) {
		$$pref1[$k]+=360;
	    }
	}
    }
# eliminate a big gap that includes zero
    $smallp=+200;
    $smalln=-200;
    for($k=0; $k<=$#$tref1;$k++) {
	next if $$pref1[$k]!~/^$float_pattern$/;
        $smallp = $$pref1[$k] if $$pref1[$k] > 0 && $$pref1[$k]<$smallp;
        $smalln = $$pref1[$k] if $$pref1[$k] < 0 && $$pref1[$k]>$smalln;
    }
    if($smallp <199 && $smalln >-199 && $smallp - $smalln > 180) {
        for($k=0; $k<=$#$tref1;$k++) {
	    next if $$pref1[$k]!~/^$float_pattern$/;
            $$pref1[$k]+=360 if $$pref1[$k]<0;
        }
    }

    &vu($tref1,$pref1,"Phase $tone2-$tone1");
}
sub phs {
#
# plot phase differences
#
    my ($i,$j,$m,$n)=@_;
    my ($tim1,$phs1,$tim2,$phs2);
    my ($tref1,$pref1,$tref2,$pref2,$k,$label);
    $#tim1=-1;
    $#phs1=-1;
    $#tim2=-1;
    $#phs2=-1;
    for($k=0; $k<$pcaln[$m][$i];$k++) {
	$tim1[$k]=$pcalt[$m][$i][$k];
	$phs1[$k]=$pcalp[$m][$i][$k];
    }
    for($k=0; $k<$pcaln[$n][$j];$k++) {
	$tim2[$k]=$pcalt[$n][$j][$k];
	$phs2[$k]=$pcalp[$n][$j][$k];
    }
    ($tref1,$pref1,$tref2,$pref2)=&match(\@tim1,\@phs1,\@tim2,\@phs2);

    for($k=0; $k<=$#$tref1;$k++) {
	if((!($$pref1[$k]=~/^$float_pattern$/)) || 
	   (!($$pref2[$k]=~/^$float_pattern$/))) {
	    $$pref1[$k]='$$$';
	} else {
	    $$pref1[$k]=$$pref2[$k]-$$pref1[$k];
	    if($$pref1[$k] > 180) {
		$$pref1[$k]-=360;
	    } elsif($$pref1[$k] < -180) {
		$$pref1[$k]+=360;
	    }
	}
    }
# eliminate a big gap that includes zero
    $smallp=+200;
    $smalln=-200;
    for($k=0; $k<=$#$tref1;$k++) {
	next if $$pref1[$k]!~/^$float_pattern$/;
        $smallp = $$pref1[$k] if $$pref1[$k] > 0 && $$pref1[$k]<$smallp;
        $smalln = $$pref1[$k] if $$pref1[$k] < 0 && $$pref1[$k]>$smalln;
    }
    if($smallp <199 && $smalln >-199 && $smallp - $smalln > 180) {
        for($k=0; $k<=$#$tref1;$k++) {
	    next if $$pref1[$k]!~/^$float_pattern$/;
            $$pref1[$k]+=360 if $$pref1[$k]<0;
        }
    }
    &vu($tref1,$pref1,"Phase diff $pcall[$n][$j]-$pcall[$m][$i]");
}

sub vup {
#
# plot something versus phase, fixed -180->+180 x axis
#
    my($x,$y, $label) = @_;
    &vuy($x,$y,$label,2);
}

sub vu {
#
# regular y value versus time plot
#
    my($x,$y, $label) = @_;
    &vuy($x,$y,$label,1);
}
sub vuy {
#
# main plot function, see vuX functions above for options
#
    my($x,$y, $label, $type) = @_;
    my ($i,$ymax,$ymin,$ply,$nfirst,$xmax,$xmin,$plx);

    if(defined($opt_g)) {
	return if $label !~/$opt_g/i;
    }
    if(defined($opt_G)) {
	return if $label =~/$opt_G/i;
    }
    $ymax=0;
    $ymin=0;
    $nfirst=0;
    for($i=0;$i<=$#$y;$i++) {
	next unless $$y[$i]=~/^$float_pattern$/ ;
	if(!$nfirst) {
	    $ymax=$$y[$i];
	    $ymin=$$y[$i];
	    $nfirst=1;
	} else {
	    $ymax=$$y[$i] if $$y[$i]>$ymax;
	    $ymin=$$y[$i] if $$y[$i]<$ymin;
	}
    }

    if($type == 2) {
	$xmax=0;
	$xmin=0;
	$nfirst=0;
	for($i=0;$i<=$#$x;$i++) {
	    next unless $$x[$i]=~/^$float_pattern$/ ;
	    if(!$nfirst) {
		$xmax=$$x[$i];
		$xmin=$$x[$i];
		$nfirst=1;
	    } else {
		$xmax=$$x[$i] if $$x[$i]>$xmax;
		$xmin=$$x[$i] if $$x[$i]<$xmin;
	    }
	}
	$xmin=-180.;
	$xmax=+180.;
    }

    if(!$first && $panel==0) {
#	print "labeling X axis\n";
	if($type == 1) {
	    pgtbox("BCTNZYH",0.0,0,"BCNVT",0.0,0);
	    pglabel("Time","","");
	} elsif($type == 2) {
	    pgbox("BCTN",45.0,0,"BCNVT",0.0,0);
	    pglabel("Phase","","");
	}
	pgask(-1);
	pgpanl(1,8);
	pgpage();
    }
    if($panel==0) {
	pgpanl(1,1);
	pgvport(0.15,.95,0,1);
	pgwindow(-0.5,16.5,0,39);
	pgtext(4,10,"$location Log Plots -- $refdate $save_file Page $page");
	$first=0;
        $page++;
    }

#    print "panel $panel\n";
    pgpanl(1,2+$panel);
    pgvport(0.15,.95,0,1);
#
# handle pathologies: all y == 0 and just one y value
#
    if($ymin == 0 && $ymax == 0) {
	$ymax = 0.001;
	$ymin =-0.001;
    } elsif($ymin == $ymax) {
	$ymax=$ymax*1.001;
	$ymin=$ymin*0.999;
        if($ymax < $ymin) {
          ($ymax,$ymin)=($ymin,$ymax);
        }
    }
#
# put edited out values at the top
#
    $good=-1;
    $bad=-1;
    for($i=0;$i<=$#$y;$i++) {
#	print "$$y[$i] ";
	if($$y[$i] =~/^$float_pattern$/) {
#	    print" matched ";
	    $plg[++$good]=$$y[$i];
	    $xg[$good]=$$x[$i];
	} else {
#	    print" not matched ";
	    $plb[++$bad]=$ymax+0.0375*($ymax-$ymin);
	    $xb[$bad]=$$x[$i];
	}
#	print " ply $ply[$i]\n";
    }
    if($type == 2) {
#
# handle pathologies: x == 0 and just one x value
#
	if($xmin == 0 && $xmax == 0) {
	    $xmax = 0.001;
	    $xmin =-0.001;
	} elsif($xmin == $xmax) {
	    $xmax=$xmax*1.001;
	    $xmin=$xmin*0.999;
	    if($xmax < $xmin) {
		($xmax,$xmin)=($xmin,$xmax);
	    }
	}
#
# put bad values on right edge
#
	$good=-1;
	$bad=-1;
	for($i=0;$i<=$#$x;$i++) {
#	print "$$x[$i] ";
	    if($$y[$i] =~/^$float_pattern$/ && $$x[$i] =~/^$float_pattern$/) {
#	    	print" matched ";
		$plg[++$good]=$$y[$i];
		$xg[$good]=$$x[$i];
	    } else {
#		print" not matched ";
		$plb[++$bad]=$$y[$i];
		$xb[$bad]=$$x[$i];
		if($$y[$i] !~/^$float_pattern$/) {
		     $plb[$bad]=$ymax+0.0375*($ymax-$ymin);
		}
		if($$x[$i] !~/^$float_pattern$/) {
		    $xb[$bad]=$xmax+0.0075*($xmax-$xmin);
		}
	    }
#	print " plx $plx[$i]\n";
	}
    }

    if($type == 1 ){
	pgswin($tmin-0.01*($tmax-$tmin),$tmax+0.01*($tmax-$tmin),
	       $ymin-0.05*($ymax-$ymin),$ymax+0.05*($ymax-$ymin));
	pgtbox("BCTZYH",0.0,0,"BCNVT",0.0,0);
	pgpoint($good+1, \@xg,\@plg,17) if $good > -1;
	if(!$giza == !($opt_Z & 2)) {
		pgpoint($bad+1, \@xb,\@plb,21) if $bad > -1;
        } else {
		pgpoint($bad+1, \@xb,\@plb,23) if $bad > -1;
        }
#    pgline($#$y+1, $x, $ply);
    } else {
	pgswin($xmin-0.01*($xmax-$xmin),$xmax+0.01*($xmax-$xmin),
	       $ymin-0.05*($ymax-$ymin),$ymax+0.05*($ymax-$ymin));
	pgbox("BCT",45.0,0,"BCNVT",0.0,0);
	pgpoint($good+1, \@xg,\@plg,17) if $good > -1;
	if(!$giza == !($opt_Z & 2)) {
		pgpoint($bad+1, \@xb,\@plb,21) if $bad > -1;
        } else {
		pgpoint($bad+1, \@xb,\@plb,23) if $bad > -1;
        }
    }
    pgvport(0.075,.95,0,1);
    pglabel("",$label,"");
    pgvport(0.15,.95,0,1);
    $panel=(++$panel)%6;
}

sub time {
#
# calculate time of the log entry
# everything is measured relative to $reftime, first time in log
# it will make a mess of $reftime is not reasonable
#
    my ($date, $other) = @_;
#2005.123.12:23:56.21
#01234567890123456789
#or
#9823717225512
#0123456789012
    if(substr($date,4,1) eq '.') {
	$year=substr($date,0,4);
	$day=substr($date,5,3);
	$hour=substr($date,9,2);
	$minute=substr($date,12,2);
	$second=substr($date,15,5);
    } else {
	$year=substr($date,0,2)+1900;
	$day=substr($date,2,3);
	$hour=substr($date,5,2);
	$minute=substr($date,7,2);
	$second=substr($date,9,4)/100;
    }
    if(!defined($refdate)) {
#	$refdate=substr($date,0,17);
	$refdate=sprintf "%04d.%03d.%02d:%02d:%05.2f",
	    $year,$day,$hour,$minute,$second;
	$refyear=$year;
	$refday=$day;
	$refhour=$hour;
	$refminute=$minute;
	$refsecond=$second;
#		print "$refyear $refday $refhour $refminute $refsecond\n";
	$tmax=0;
	$tmin=3e7;
    }
    $days_in_year=365;
    $days_in_year=366 if $refyear%4==0;
    $time=(((($year-$refyear)*$days_in_year
	     +$day-$refday)*24.0
	    +$hour-$refhour)*60.0
	   +$minute-$refminute)*60.0
	   +$second-$refsecond;
#	    print "$day $hour $minute $second $time\n";
    if(!defined($other)) {
        $tmax=$time if $time>$tmax;
        $tmin=$time if $time<$tmin;
    }
    return $time;
}
sub by_rdbe_chan {
#by band, then by IF, then by channel
    substr($a,2,1) cmp substr($b,2,1) or
    substr($a,3,1) cmp substr($b,3,1) or
    substr($a,0,2) cmp substr($b,0,2);
}
sub by_dbbc3_chan {
my($if_a,$if_b);
#by IF, then by side-band, then by channel (whole IFs after channels)
    if(substr($a,0,1) eq 'i' && substr($b,0,1) eq 'i') {
      return $a cmp $b;
    } elsif(substr($a,0,1) eq 'i') {
	$if_b=(substr($b,0,3)-1)/8 % 8;
	$if_a=index('abcdefgh',substr($a,1,1),);
	if ($if_a eq $if_b) {
	    return +1;
	}
	return $if_a <=> $if_b;
    } elsif(substr($b,0,1) eq 'i') {
	$if_a=(substr($a,0,3)-1)/8 % 8;
	$if_b=index('abcdefgh',substr($b,1,1),);
	if ($if_a eq $if_b) {
	    return -1;
	}
	return $if_a <=> $if_b;
    } else {
	$if_a=(substr($a,0,3)-1)/8 % 8;
	$if_b=(substr($b,0,3)-1)/8 % 8;
	return ($if_a <=> $if_b or
	    substr($a,3,1) cmp substr($b,3,1) or
	    $a cmp $b);
    }
}
sub by_rdbe_tone {
#by band, then by IF, then by tone
    substr($a,1,1) cmp substr($b,1,1) or
    substr($a,0,1) cmp substr($b,0,1) or
    substr($a,2) cmp substr($b,2);
}
sub by_clocks {
#For RDBEs by type, then by band then by (reverse) origin
#For DBBC3 by IF, then (reverse) origin
# all others alphabetize
    if(substr($a,0,2) eq 'rd' and substr($b,0,2) eq 'rd') {
       return (substr($a,-3) cmp substr($b,-3) or
           substr($a,4,1) cmp substr($b,4,1) or
           $b cmp $a);
    } elsif(substr($a,0,11) eq 'mcast_time/' and substr($b,0,8) eq 'pps2dot/') {
       return (substr($a,-1) cmp substr($b,-1) or 1);
    } elsif(substr($b,0,11) eq 'mcast_time/' and substr($a,0,8) eq 'pps2dot/') {
       return (substr($a,-1) cmp substr($b,-1) or -1);
    } else {
       return( $a cmp $b);
    }
}
#
# main program
#

# 1.0 Initialize

use PGPLOT;
require "getopts.pl";

pgqinf("VERSION",$val,$len);
$giza = $val =~ /giza/;

if (!&Getopts("12:abc:d:e:f:g:hjk:l:m:nprs:t:u:vwz:BCD:G:M:ST:VWYZ:")) {
  print STDERR "For help, try: 'plotlog -h'\n";
  exit -1;
}

$float_pattern = '([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?';
@hex =("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f");

if ($#ARGV < 0 &&!defined($opt_h) &&!defined($opt_V)) {
    $ARGV[0]=`lognm`;
    chomp $ARGV[0];
    if ($ARGV[0] eq "") {
        print STDERR "No log files specified and the FS is not running.\n";
        print STDERR "For help, try: 'plotlog -h'\n";
        exit 0;
    }
    $ARGV[0]="/usr2/log/$ARGV[0].log";
}

if(defined($opt_V)) {
    print "[plotlog 2.4]\n";
    pgqinf("VERSION",$val,$len);
    print "using PGPLOT module version $PGPLOT::VERSION, PGPLOT $val library\n";
    exit 0;
}

if (defined($opt_h)) {
    print "Usage: plotlog [options] [logs]

Synopsis: extracts and plots data from the log files
Exit code is 0 for no error, -1 for no logs found, -2 for nothing to plot.

If no 'logs' are specified and the FS is running, the current FS log is used.
If multiple logs are specified, they are plotted combined with a time axis that
spans all the data. The log file name used in the plot titles is the first
log encountered, but can be overridden with the '-T' option.

This script makes plots of data found in the log files. It plots wx/, cable/,
cdms/, tsys/ rx/ (and sk/ and sx/), clock (see below), setcl#time/, RDBE
phase-cal, recorder statistics, and the first encountered tone per channel (LSB
and USB separately) for Mark IV decoder phase-cal data. The time axes of all
the plots for a given log are the same. Valid data is plotted so that it fits
entirely inside the plotting area. Points that appear along the top edge of a
plot as open circles did not decode (\$\$\$ for example) or were edited out
automatically or by command line options. Cases where the form of the entry was
too garbled to be identified or the sampling failed (possibly because the
command timed-out) do not appear at all. The latter two problems may be
noticeable if the plot for a data type is missing entirely or is sparser than
expected.

Clock data includes values from any suitable commands containing 'maser',
'fmout', and/or 'gps' in their names, as well as RDBE GPS and PPS offsets, and
DBBC3 'pps2dot' data. For RDBEs (and DBBC3s), if both multicast and monitor
(DBBC3: 'mcast_time') values are present, only the multicast values will be
plotted unless the '-B' option is specified in which case both will be plotted.

For RBDEs, unless the '-d' and/or '-D' options are specified, only the first
phase-cal tone encountered for each IF is plotted. Any Perl regular expressions
may be used for the arguments. You can use these options to get different
slices of tones, e.g., '-d 0005' for the 5-th tone for all bands or '-d 1a' for
all tones in band A, IF 1. You can also use '|' (or) to match multiple strings,
e.g. '-d \"1a0005|1a0010\"'. The '-D' option is applied after '-d'.

For RDBEs and DBBC3s, unless the '-m' and/or '-M' options are specified, only
Tsys from the first (non-'00') detector encountered for each IF is plotted. Any
Perl regular expressions may be used for the arguments. You can use these
options to get different slices of detectors, e.g., '-m 01' for the '01'
detectors on all bands or '-m a1' for all detectors from band A IF 1. You can
also use '|' (or) to match multiple strings, e.g., '-m \"1a0005|1a0010\"'. The
'-M' option is applied after '-m'.

The '-g' and/or '-G' options can be specified to select only certain plots
based on their titles. Any Perl regular expressions may be used for the
arguments. You can also use '|' (or) to match multiple strings, e.g.,
'-g \"Temp|Hum\"'. The '-G' option is applied after '-g'.

The '-d'/'-D' and/or '-m/'-M' options can be combined with '-g'/'-G'. For
example, '-d 0210 -g 0210' will produce only plots of the 210 phase cal tone
for each band. Whereas, '-d 0210' will produce the 210 phase-cal tone plots and
all non-phase-cal plots.

Cable and clock data with absolute values greater 10 are automatically deleted
unless the '-C' option is used.

The recorder statistics plotted are the delay in starting, shortness of the
recording, and missing bytes, as determined from the scan_check results. If the
scan_check is missing or has bad values, the point is shown at the top edge of
the plot.

The '-1anw' options can be used to select different IF arrangements for
plotting phase differences between phase-cal tones for back-ends with
baseband/video converters. Any combination of the '-1anw' options can be used.
No difference is plotted more than once. Only one difference between each pair
of converters is plotted. The differences between the two side-bands of one
converter are plotted separately after the differences between converters.

The '-v' option can be used to plot phase differences within IFs for RDBEs.

The '-j' option (not yet supported for RDBEs) can be used to normalize
phase-cal amplitudes by Tsys (per channel) for time plots and amplitude versus
phase plots. The minimum non-zero Tsys (per channel) is used as the reference
value for normalization. If this causes problems, an explicit global reference
value can be specified with the '-k' option. The '-t' option may also be useful
for removing bad Tsys values. By default, dual side-band Tsys is used for
normalization of Mark IV VC data if it is available, if not then single
side-band Tsys data is used. This is usually what is wanted but if not, the
'-b' option can be used to force use of single side-band Tsys for channels for
which that it is available.

For the '-j1anvw' options, a simple model is used to match different data from
the log. Points from different data types that are the nearest together in time
are matched. No epoch from one data type is matched to more than one epoch of
another type in this way. If the resulting matched epochs are within 60 seconds
of each other, the match is accepted. The time tolerance of the match can be
adjusted using the '-u' option.

The '-p' option adds amplitude versus phase plots for each phase-cal tone after
all other plots. This option suppresses all other output except options
'-1anvw'.

'cable/' and 'cdms/' data are plotted as one-way cable delay change unless the
'-r' (raw) option is used.

The plotting support is different if 'giza' has been substituted for 'pgplot'
(FSL11 may have 'giza-1.2.0'). You can see if 'giza' is being used by checking
if the 'PGPLOT' library version shown by 'plotlog -V' includes the string
'giza'. If the script detects that 'giza' is in use, it makes two adjustments:
(i) the line width is set to '0' and (ii) the bad points are plotted with
larger open circles. However, the line width is not set to '0' for plot device
'/xw' because that makes the plot borders disappear. If these adjustments cause
problems or are no longer needed because 'giza' has been brought into better
agreement with 'pgplot', they can be disabled, individually, with the '-Z hex'
option (see below). If 'giza' is not detected, the '-Z hex' option can be used
to force the adjustments. Additionally, the 'giza' library does not respect the
'PGPLOT_BACKGROUND' and 'PGPLOT_FOREGROUND' environment variables.

Option explanations:

 Generally, the philosophy is that if no options are specified the script
 should do something that is likely to be useful. Options can be added to tune
 the behavior for different situations. Scripts or aliases can be used if any
 options are needed routinely.

 phase-difference plot options (suppresses all other output, except '-p'):
  -1 all converters are on one IF
  -a odd converters are on one IF, even are on another (astronomy)
  -n converters 1-8 on one band, 9-16 on a second (narrow band geodesy),
     also useful for wide band geodesy
  -v differences within RDBE (VGOS) IFs
  -w converters 1-4 on one band, 5-8 on a second, 9-16 on a third
     (wide band geodesy)
  Phase differences between LSB/USB channels for the same BBC/VC are not
  displayed. Points that appear along the right edge of the plot as open
  circles had phase values that did not decode (\$\$\$ for example) or were
  edited out by command line options.

 -b        force same side-band Tsys normalization (reject dual and opposite
           side-band Tsys even if that is all that is available)
 -c value  cable cal edit, if greater than zero, delete points that much or
           more above lowest value, if less than zero, delete points that much
           or more below largest value; uses plot Y axis units, be careful of
           the -r option
 -C        don't automatically delete cable and clock data with raw values that
           have absolute values greater than 10
 -d regexp for RDBEs only, include only phase-cal tones that DO match 'regexp',
           where 'regex' is any Perl regular expression pattern. You will need
           to quote 'regexp' if it contains special characters. The match is
           case insensitive. This option can be combined with the '-D' option.
 -D regexp for RDBEs only, include only phase-cal tones that do NOT match
           'regexp', see '-d'
 -e string specify rack, useful for snippets of logs for DBBC3s and RDBEs that
           don't have an 'equip' line near the start; the only useful values
           are 'dbbc3' and 'rdbe', which are case insensitive.
 -g regexp include only plots with titles that DO match 'regexp', where
           'regexp' is any Perl regular expression pattern. You will need to
           quote 'regexp' if it contains special characters. This option can be
           combined with the '-G' option. The match is case insensitive.
 -G regexp include only plots with titles that DO NOT match 'regexp', see '-g'
 -j        normalize phase-cal amp by Tsys per channel (not supported for
           RDBEs yet).
 -k value  changes 'j' normalization from minimum non-zero Tsys per channel to
           'value' for all channels (not supported for RDBEs, see '-j')
 -l string specify location, useful for snippets of logs that don't include the
           'location' line. This is used in plot titles.
 -m regexp for RDBEs and DBBC3s only, include only Tsys detectors that DO
           match 'regexp', where 'regex' is any Perl regular expression
           pattern. You will need to quote 'regexp' if it contains special
           characters. This option can be combined with the '-M' option. The
           match is case insensitive.
 -M regexp for RDBEs and DBBC3s only, include only Tsys detectors that do NOT
           match 'regexp', see '-m'
 -p        plot amplitude versus phase for phase cal tones,
           suppresses all other output except -1anw
 -r        plot 'cable/' and 'cdms/'  data in raw units
 -s string match commands with 'string' instead of 'tsys', option '-t' still
           applies for editing
 -2 n      n is '1' or '2', useful for selecting which values to plot for
           '-s \"tpicd#tpcont\"'; '1' for the first value (usually cal-on), '2'
           (usually cal-off for the second
 -t value  edit out tsys points less than or equal to zero or greater than or
           equal to 'value'
 -z value  edit out phase-cal data with amplitudes less than 'value'
 -u value  maximum distance in time (seconds) allowed for matching data for
           'j1anvw' option plots is changed to 'value' instead of 60 seconds
 -S        require a leading slash for wx/, cable/, and cdms/ entries
 -T string replace log file name with 'string' in title, mostly useful when
           combining logs
 -V        print program and PGPLOT version information and stop
 -W        plot all '/rx/' fields of the form 'number[...]'
 -Y        don't edit out phase-cal with phases outside [-180,+180] degrees
 -Z hex    toggle 'giza' adjustments, selected bitwise, LSB is bit 0; if
           'giza' is detected, this disables them; if 'giza' is not detected,
           it enables them:
             bit 0: set line width to 0
             bit 1: increase bad point open circle size
           thus with 'giza' detected, '-Z 2' removes the latter
 -h        print this help information and stop
 -f file/device  send graphs to 'file' using PGPLOT 'device'
    if -f omitted and DISPLAY is defined, '/xw' will be used
    if -f omitted and DISPLAY is not defined, 'plotlog.ps/vps' will be used
    if '-f ?', you will be prompted for standard pgplot devices,
               be sure to quote '?', like \\?
    'vps' (portrait PostScript) is a useful choice for 'device' for file output
    '.ps' works well as the file extension for device 'vps'

You can use different colors (but not with 'giza') by setting the environment
variables 'PGPLOT_BACKGROUND' and 'PGPLOT_FOREGROUND' appropriately before
running the program. Colors that are available depend on the device, but in
addition to 'black' and 'white', may include 'red', 'green', 'blue', 'cyan',
'magenta', 'yellow', 'grey', 'slategrey', 'gold', and maybe others.
";
    exit 0;
}

if(defined($ENV{'DISPLAY'}) && !defined($opt_f)) {
    $dev="/xw";
} elsif(!defined($opt_f)) {
    $dev="plotlog.ps/vps";
} else {
    $dev=$opt_f;
}

$string="tsys";
$string = $opt_s if defined($opt_s);

$time_dist=60;
$time_dist = $opt_u if defined($opt_u);

if(defined($opt_l)) {
    $location=$opt_l;
}

if(defined($opt_e)) {
    $rdbe=$opt_e=~/rdbe/i;
    $dbbc3=$opt_e=~/dbbc3/i;
    $equip=1;
}

# 2.0 extract data

$some=0;
$first=1;
$wxcount=0;
$recordn=0;

foreach $file (@ARGV) {
    if(!defined($save_file)) {
        if($opt_T) {
            $save_file=$opt_T;
        } else {
            $save_file=$file;
        }
    }
    open(FILE,$file) || do {
	print STDERR "can't open $file: $!\n";
	next;
    };
    $some=1;
#   print "file $file \n";
    $x=0;
    $y=0;
    $slash="\/" if $opt_S;
    while (<FILE>) {
	s/\r?\n\z//s;
	if(!$location && /;location,([^,]*)/i) {
	    $location=$1;
	    (undef)=&time($_);
	}
	if(!$equip && /;equip,/i) {
	    $equip=1;
	    $rdbe=/rdbe/;
	    $dbbc3=/dbbc3/;
	}
	if(/${slash}wx\/(.*)/i) {
	    @fields=split(',',$1);
	    s{^\s+|\s+$}{}g foreach @fields;
	    $temp[$wxcount]=shift(@fields);
	    $pres[$wxcount]=shift(@fields);
	    $humid[$wxcount]=shift(@fields);
	    $windsp[$wxcount]=shift(@fields) if 3 <= tr/,//;
	    $winddir[$wxcount]=shift(@fields) if 4 <= tr/,//;
	    $wxtime[$wxcount]=&time($_);
	    $wxcount++;
	} elsif(/${slash}cable\/[ ]*([^, ]*)/i) {
	    $value=$1;
	    $cable[$cablen]=$value;
	    $cablet[$cablen]=&time($_);
	    $cablen++;
	} elsif(/${slash}cdms\/[ ]*([^, ]*)/i) {
	    $value=$1;
	    $value= '$$$' if $value > '999998.5';
	    $cdms[$cdmsn]=$value;
	    $cdmst[$cdmsn]=&time($_);
	    $cdmsn++;
	} elsif(/pcalports[=\/]([^,]*),([0-9]+)/i||/vsi4[=\/][^,]*,([^,]*),([0-9]+)/i) {
	    $x=$1;
	    $y=$2;
#	    print "pcalports $1 $2\n";
	} elsif (/decode4\/[p]*cal ([lu])sb([xy])/i &&defined($x)) {
	    $sb = $1;
	    $port=$2;
#	    print "sb $sb port $2\n";
	    if($sb eq "u") {
		$band=1;
	    } elsif($sb eq "l") {
		$band=0;
	    } else {
		die "unknown side-band $sb\n";
	    }
	    if($port eq "x") {
		$chan=$x;
	    } elsif ($port eq "y") {
		$chan=$y;
	    } else {
		die "unknown pcal port $port\n";
	    }
	    (undef,undef,@fields)=split(' ');
	    next if($#fields!=3);
	    $freq=shift(@fields);
	    $rate=shift(@fields);
	    $amp=shift(@fields);
	    $phase=shift(@fields);
	    if(!defined($pcalf[$band][$chan])) {
		$pcalf[$band][$chan]=$freq;
	    } elsif($pcalf[$band][$chan]!=$freq) {
		next;
	    }
	    if(defined($opt_z) && $amp < $opt_z) {
		$amp='$$$';
		$phase='$$$';
	    }
	    if(!defined($opt_Y) && ($phase < -180 || 180 < $phase)) {
		$phase='$$$';
	    }
	    $pcalt[$band][$chan][$pcaln[$band][$chan]]=&time($_);
	    $pcala[$band][$chan][$pcaln[$band][$chan]]=$amp;
	    $pcalp[$band][$chan][$pcaln[$band][$chan]]=$phase;
	    if($chan > -1 && $chan < 16 && !defined($pcall[$band][$chan])) {
		$pcall[$band][$chan]="$hex[$chan]$sb";
	    }
	    $pcaln[$band][$chan]++;
	} elsif (/pcaldisp2\/k5ts/i) {
	    (undef,undef,@fields)=split('[ /]+');
	    $rec=substr($fields[0],-1);
            $ch=substr($fields[1],-1);
	    $chan = 4*($rec-1)+$ch;
            if(substr($fields[3],-3) ne "bit") {
		$fields[3] =~ s/hz//i;
		$freq = $fields[3];
		$iof=1;
	    } else {
		$freq = 10000;
		$iof=0;
	    }
	    $sb=substr($fields[4+$iof],0,1);
	    $amp=$fields[5+$iof];
	    $phase=$fields[6+$iof];
#	    print "chan $chan freq $freq sb $sb amp $amp phase $phase\n";
#	    print "sb $sb port $2\n";
	    if($sb eq "u") {
		$band=1;
	    } elsif($sb eq "l") {
		$band=0;
	    } else {
		die "unknown side-band $sb\n";
	    }
	    if(!defined($pcalf[$band][$chan])) {
		$pcalf[$band][$chan]=$freq;
	    } elsif($pcalf[$band][$chan]!=$freq) {
		next;
	    }
	    if(defined($opt_z) && $amp < $opt_z) {
		$amp='$$$';
		$phase='$$$';
	    }
	    if(!defined($opt_Y) && ($phase < -180 || 180 < $phase)) {
		$phase='$$$';
	    }
	    $pcalt[$band][$chan][$pcaln[$band][$chan]]=&time($_);
	    $pcala[$band][$chan][$pcaln[$band][$chan]]=$amp;
	    $pcalp[$band][$chan][$pcaln[$band][$chan]]=$phase;
	    $pcall[$band][$chan]="$chan$sb";
	    $pcaln[$band][$chan]++;
	} elsif (/[#\/]pcal\/(.*)/i) {
	    @fields=split(',',$1);
#	    print "$_\n";
	    s{^\s+|\s+$}{}g foreach @fields;
#	    print "@fields \n\n";
	    while($tone=shift(@fields)) {
		$amp=shift(@fields);
		$phase=shift(@fields);
		if($rdbe) {
		    if(defined($opt_d)) {
			next if $tone !~/$opt_d/i;
		    }
		    if(defined($opt_D)) {
			next if $tone =~/$opt_D/i;
		    }
		    if(!defined($opt_d) && !defined($opt_D)) {
			$if_tone=substr($tone,0,2);
			if(!$rdbe_if_pcal{$if_tone}){
			    $rdbe_if_pcal{$if_tone}{$tone}=1;
			} elsif(!$rdbe_if_pcal{$if_tone}{$tone}) {
			    next;
			}
		    }
		} elsif(defined($opt_d) || defined($opt_D)) {
		    die "Options -d and -D are only for RDBE racks\n";
		}
		if(!defined($opt_Y) && ($phase < -180 || 180 < $phase)) {
		    $phase='$$$';
		}
		$rpcalt{$tone}[$rpcaln{$tone}]=&time($_);
		$rpcala{$tone}[$rpcaln{$tone}]=$amp;
		$rpcalp{$tone}[$rpcaln{$tone}]=$phase;
		$rpcaln{$tone}++;
#		print "$tone $rpcala{$tone} $rpcalp{$tone} $rpcaln{$tone}\n";
	    }
	} elsif (/[#\/]$string\/(.*)/i) {
	    @fields=split(',',$1);
	    s{^\s+|\s+$}{}g foreach @fields;
	    while($detect=shift(@fields)) {
		$tsys=shift(@fields);
		if($rdbe) {
		    if(defined($opt_m)) {
			next if $detect !~/$opt_m/i;
		    }
		    if(defined($opt_M)) {
			next if $detect =~/$opt_M/i;
		    }
		    if(!defined($opt_m) && !defined($opt_M)) {
                        next if '00' eq substr($detect,0,2);
			$if_detect=substr($detect,2,2);
			if(!$rdbe_if_tsys{$if_detect}){
			    $rdbe_if_tsys{$if_detect}{$detect}=1;
			} elsif(!$rdbe_if_tsys{$if_detect}{$detect}) {
			    next;
			}
		    }
		} elsif($dbbc3) {
		    if(defined($opt_m)) {
			next if $detect !~/$opt_m/i;
		    }
		    if(defined($opt_M)) {
			next if $detect =~/$opt_M/i;
		    }
		    if(!defined($opt_m) && !defined($opt_M)) {
			if(substr($detect,0,1) ne 'i') {
			    $if_detect=(substr($detect,0,3)-1)/8 % 8;
			} else {
			    $if_detect=index('abcdefgh',substr($detect,1,1),);
			}
			if(!$dbbc3_if_tsys{$if_detect}){
			    $dbbc3_if_tsys{$if_detect}{$detect}=1;
			} elsif(!$dbbc3_if_tsys{$if_detect}{$detect}) {
			    next;
			}
		    }
		} elsif(defined($opt_m) || defined($opt_M)) {
		    die "Options -m and -M are only for RDBE and DBBC3 racks\n";
		}
		if(defined($opt_2)) {
		    if($opt_2 == 1) {
			$detect="$detect (1)";
			shift(@fields);
		    } elsif(@fields) {
			$detect="$detect (2)";
			$tsys=shift(@fields);
		    } else {
			next;
		    }
		}
#		print "$detect $tsys\n";
		if(defined($opt_t)) {
		    $tsys= '$$$' if $tsys <= 0;
                    $tsys= '$$$' if $tsys >= $opt_t;
		}
		$tsysv{$detect}[$tsysn{$detect}]=$tsys;
		$tsyst{$detect}[$tsysn{$detect}]=&time($_);
#		print "tsys $tsysv{$detect}[$tsysn{$detect}]\n" if($detect eq '1u');
		$tsysn{$detect}++;	 
	    }
	} elsif (/\/tsys1=/i) {
	    (undef,undef,@fields)=split('[/=,]');
	    $det=0;
	    while($device=shift(@fields)) {
		$det++;
#		print "$det $device\n";
		$tsys1{$det}=sprintf "%3s", $device;
	    }
	} elsif (/\/tsys1\//i) {
	    (undef,undef,@fields)=split('[/,]');
	    $det=0;
	    while($tsys=shift(@fields)) {
		$det++;
		$detect=$tsys1{$det};
#		print "$detect $tsys\n";
		if(defined($opt_t)) {
		    $tsys= '$$$' if $tsys <= 0;
                    $tsys= '$$$' if $tsys >= $opt_t;
		}
		$tsysv1{$detect}[$tsysn1{$detect}]=$tsys;
		$tsyst1{$detect}[$tsysn1{$detect}]=&time($_);
#		print "tsys $tsysv1{$detect}[$tsysn1{$detect}]\n" if($det ==1);
		$tsysn1{$detect}++;	 
	    }
	} elsif (/\/tsys2=/i) {
	    (undef,undef,@fields)=split('[/=,]');
	    $det=0;
	    while($device=shift(@fields)) {
		$det++;
#		print "$det $device\n";
		$tsys2{$det}=sprintf "%3s", $device;
	    }
	} elsif (/\/tsys2\//i) {
	    (undef,undef,@fields)=split('[/,]');
	    $det=0;
	    while($tsys=shift(@fields)) {
		$det++;
		$detect=$tsys2{$det};
#		print "$detect $tsys\n";
		if(defined($opt_t)) {
		    $tsys= '$$$' if $tsys <= 0;
                    $tsys= '$$$' if $tsys >= $opt_t;
		}
		$tsysv2{$detect}[$tsysn2{$detect}]=$tsys;
		$tsyst2{$detect}[$tsysn2{$detect}]=&time($_);
#		print "tsys $tsysv2{$detect}[$tsysn2{$detect}]\n" if($det ==1);
		$tsysn2{$detect}++;	 
	    }
	} elsif (/\/tsys3=/i) {
	    (undef,undef,@fields)=split('[/=,]');
	    $det=0;
	    while($device=shift(@fields)) {
		$det++;
#		print "$det $device\n";
		$tsys3{$det}=sprintf "%3s", $device;
	    }
	} elsif (/\/tsys3\//i) {
	    (undef,undef,@fields)=split('[/,]');
	    $det=0;
	    while($tsys=shift(@fields)) {
		$det++;
		$detect=$tsys3{$det};
#		print "$detect $tsys\n";
		if(defined($opt_t)) {
		    $tsys= '$$$' if $tsys <= 0;
                    $tsys= '$$$' if $tsys >= $opt_t;
		}
		$tsysv3{$detect}[$tsysn3{$detect}]=$tsys;
		$tsyst3{$detect}[$tsysn3{$detect}]=&time($_);
#		print "tsys $tsysv3{$detect}[$tsysn3{$detect}]\n" if($det == 1);
		$tsysn3{$detect}++;	 
	    }
	} elsif (/\/rx\/.*$float_pattern\[[^]]+\]/i) {
# Wettzell
	    (undef,$cmd,@fields)=split('[/,]');
	    $device=$fields[0];
	    $wettzell="dBm|degC";
	    $wettzell="[^]]*" if $opt_W;
	    while($param=shift(@fields)) {
		if($param =~ /(.*)\[($wettzell)\]/) {
		    next if !$opt_W && $2 eq "dBm" && $device =~ /^lo/;
		    $subdevice="$device \($2\)";
		    $rx{$subdevice}[$rxcount{$subdevice}]=$1;
		    $rxtime{$subdevice}[$rxcount{$subdevice}]=&time($_);
		    $rxcount{$subdevice}++;
		    $rxunits{$subdevice}="";
		    $rxcmd=$cmd;
		 }
	    }
	} elsif (/\/(sx|rx|sk)\//i) {
# Everyone else
	    (undef,$cmd,@fields)=split('[/,]');
	    ($value,$units)=split(' ',$fields[$#fields]);
	    next if ! $units; # so we don't get bad entries for Wettzell
	    ($rx{$fields[0]}[$rxcount{$fields[0]}],undef)=$value;
	    $rxtime{$fields[0]}[$rxcount{$fields[0]}]=&time($_);
#	    print "$fields[0] $rx{$fields[0]}[$rxcount{$fields[0]}]\n";
	    $rxcount{$fields[0]}++;
	    $rxunits{$fields[0]}=$units;
	    $rxcmd=$cmd;
	} elsif(/#setcl#time\//i) {
		(undef,@fields)=split('[/,]');
		$setcl[$setcln]=$fields[9];
		$setclt[$setcln]=&time($_);
		$setcln++;
	} elsif(/(rdtc.#dot2[gp]ps)/) {
	    $cmd=$1;
	    (undef,@fields)=split('[/,]');
	    $clockv{$cmd}[$clockn{$cmd}]=$fields[0];
	    $clockt{$cmd}[$clockn{$cmd}]=&time($_);
	    $clockn{$cmd}++;
	} elsif(/\/(rdbe.\/!dbe_[gp]ps)/) {
	    $cmd=$1;
	    (undef,undef,undef,@fields)=split('[:;]');
	    $clockv{$cmd}[$clockn{$cmd}]=$fields[0];
	    $clockt{$cmd}[$clockn{$cmd}]=&time($_);
	    $clockn{$cmd}++;
	} elsif(/dbtcn#(pps2dot)/) {
	    $cmdp=$1;
	    (undef,@fields)=split('[/,]');
	    s{^\s+|\s+$}{}g foreach @fields;
	    while($if_chan=shift(@fields)) {
		$cmd=$cmdp . '/' . $if_chan;
		$delay=shift(@fields);
		$clockv{$cmd}[$clockn{$cmd}]=$delay;
		$clockt{$cmd}[$clockn{$cmd}]=&time($_);
		$clockn{$cmd}++;
	    }
	} elsif(/\/mcast_time\//) {
	    (undef,$cmd,@fields)=split('[/,]');
	    s{^\s+|\s+$}{}g foreach @fields;
            next if $fields[0] eq '0';
	    $cmd=$cmd . '/' . $fields[0];
	    $clockv{$cmd}[$clockn{$cmd}]=$fields[3];
	    $clockt{$cmd}[$clockn{$cmd}]=&time($_);
	    $clockn{$cmd}++;
	} elsif(/\/(.*(fmout|maser|gps).*)\//i) {
	    $cmd=$1;
	    (undef,undef,@fields)=split('[/,]');
	    s{^\s+|\s+$}{}g foreach @fields;
	    $clockv{$cmd}[$clockn{$cmd}]=$fields[0];
	    $clockt{$cmd}[$clockn{$cmd}]=&time($_);
	    $clockn{$cmd}++;
	} elsif(/#.....#(.*(fmout|maser|gps).*)\//i) {
	    $cmd=$1;
	    (undef,@fields)=split('[/,]');
	    s{^\s+|\s+$}{}g foreach @fields;
	    $clockv{$cmd}[$clockn{$cmd}]=$fields[0];
	    $clockt{$cmd}[$clockn{$cmd}]=&time($_);
	    $clockn{$cmd}++;
        } elsif(/:scan_name=([^,]*),([^,]*),([^,]*),([^,]*),([^,]*)/) {
	    $new_dur=$5;
        } elsif(/:!(\d{4}\.\d{3}\.\d\d:\d\d:\d\d)/) {
	    $new_time=&time("$1.00",1);
	} elsif(/:disk_record=on/) {
	    $nom_start=$new_time;
	    $nom_dur=$new_dur;
	    $recordt[$recordn]=$nom_start;
	    $recordn++;
        } elsif(defined($nom_start) && /scan_check\/.*,(\d+)y(\d+)d(\d+)h(\d+)m([\d.]+)s,([\d.]+)s,[^,]*,(\d+)/) {
	    $act_start=&time(sprintf("%0.4d.%0.3d.%0.2d:%0.2d:%05.2f",
			  $1,$2,$3,$4,$5),1);
	    $act_dur=$6;
	    $missing=$7;
	    $delay = $act_start-$nom_start;
	    $short=$nom_dur-$act_dur;
	    next if $act_start < $nom_start;
	    $recordd[$recordn-1]=$delay;
	    $records[$recordn-1]=$short;
	    $recordm[$recordn-1]=$missing;
        } elsif(/:mk6[ab]?=record=(\d+)y(\d+)d(\d+)h(\d+)m(\d+)s:(\d+)/) {
	    $nom_start=&time(sprintf("%0.4d.%0.3d.%0.2d:%0.2d:%05.2f",
			  $1,$2,$3,$4,$5),1);
	    $nom_dur=$6;
	    $recordt[$recordn]=$nom_start;
	    $recordn++;
        } elsif(defined($nom_start) && ( /!scan_check\?.*:(\d+)y(\d+)d(\d+)h(\d+)m(\d+)s:([-\d.]+):[^:]*:[^:]*:(\d+)/ ||
                                 /!scan_check\?.*:\s*(\d+)y(\d+)d(\d+)h(\d+)m([\d.]+)s\s*:\s*([-\d.]+)s\s*:[^:]*:\s*(\d+)/ )) {
	    $act_start=&time(sprintf("%0.4d.%0.3d.%0.2d:%0.2d:%05.2f",
			  $1,$2,$3,$4,$5),1);
	    $act_dur=$6;
	    $missing=$7;
	    $delay = $act_start-$nom_start;
            if($act_dur >= 0) {
	        $short=$nom_dur-$act_dur;
            } else {
	        $short='$$$';
            }
	    next if $act_start < $nom_start;
	    $recordd[$recordn-1]=$delay;
	    $records[$recordn-1]=$short;
	    $recordm[$recordn-1]=$missing;
	}
    }
}
exit -1 if(!$some);

pgbegin(0,$dev,1,8);
$opt_Z |= 1 if $giza && $dev eq '/xw';
pgslw(0) if !$giza != !($opt_Z & 1);
pgsch(3);
pgask(-1);
pgpanl(1,8);
pgpage();

$page=1;
$panel=0;
$first=1;

goto EXTRAS
    if defined($opt_1)||defined($opt_a)||defined($opt_n)||defined($opt_w)||defined($opt_v)||defined($opt_p);

if(@wxtime) {
    &vu(\@wxtime, \@temp,"Temperature (C)");
    &vu(\@wxtime, \@pres,"Pressure (millibars)");
    &vu(\@wxtime, \@humid,"Humidity (%)");
    &vu(\@wxtime, \@windsp,"Wind Speed (m/s)") if @windsp;
    &vu(\@wxtime, \@winddir,"Wind Dir (deg Az)") if @winddir;
}

if(@cablet) {
    if(!defined($opt_r)) {
        for($i=0;$i<=$#cable;$i++) {
	    next unless $cable[$i]=~/^$float_pattern$/ ;
	    if(defined($opt_C) || abs($cable[$i])<1e1) {
		$cablefirst=$cable[$i] if(!defined($cablefirst));
		$cable[$i]=($cable[$i]-$cablefirst)/4e5;
		$cable[$i]/=1e-12;
	    } else {
		$cable[$i]= '$$$';
	    }
	}
	if(defined($opt_c)) {
	    for($i=0;$i<=$#cable;$i++) {
		next unless $cable[$i]=~/^$float_pattern$/ ;
		$cablemax=$cable[$i] if(!defined($cablemax));
		$cablemin=$cable[$i] if(!defined($cablemin));
		if($cablemax <$cable[$i]) {
		    $cablemax=$cable[$i];
		} elsif($cablemin > $cable[$i]) {
		    $cablemin=$cable[$i];
		}
	    }
#	    printf "$cablemax $cablemin $opt_c\n";
	    for($i=0;$i<=$#cable;$i++) {
		next unless $cable[$i]=~/^$float_pattern$/ ;
		if($opt_c < 0 && $cable[$i] < $cablemax+$opt_c) {
		    $cable[$i]='$$$';
                } elsif($opt_c >0 && $cable[$i] > $cablemin+$opt_c) {
		    $cable[$i]='$$$';
                }
	    }
	}
	&vu(\@cablet, \@cable,"Cable Delay (ps)");
    } else {
        for($i=0;$i<=$#cable;$i++) {
	    next unless $cable[$i]=~/^$float_pattern$/ ;
	    if(defined($opt_C) || abs($cable[$i])<1e1) {
		$cable[$i]*=1e3;
	    } else {
		$cable[$i]= '$$$';
	    }
	}
	if(defined($opt_c)) {
	    for($i=0;$i<=$#cable;$i++) {
		next unless $cable[$i]=~/^$float_pattern$/ ;
		$cablemax=$cable[$i] if(!defined($cablemax));
		$cablemin=$cable[$i] if(!defined($cablemin));
		if($cablemax <$cable[$i]) {
		    $cablemax=$cable[$i];
		} elsif($cablemin > $cable[$i]) {
		    $cablemin=$cable[$i];
		}
	    }
#	    printf "$cablemax $cablemin $opt_c\n";
	    for($i=0;$i<=$#cable;$i++) {
		next unless $cable[$i]=~/^$float_pattern$/ ;
		if($opt_c < 0 && $cable[$i] < $cablemax+$opt_c) {
		    $cable[$i]='$$$';
                } elsif($opt_c >0 && $cable[$i] > $cablemin+$opt_c) {
		    $cable[$i]='$$$';
                }
	    }
	}
        &vu(\@cablet, \@cable,"Cable Counter (ms)");
    }
}
if(@cdmst) {
    if(!defined($opt_r)) {
        for($i=0;$i<=$#cdms;$i++) {
	    next unless $cdms[$i]=~/^$float_pattern$/ ;
	    $cdmsfirst=$cdms[$i] if(!defined($cdmsfirst));
	    $cdms[$i]=0.5*($cdms[$i]-$cdmsfirst);
	}
	&vu(\@cdmst, \@cdms,"CDMS delay (ps)");
    } else {
	&vu(\@cdmst, \@cdms,"CDMS raw (ps)");
    }
}
foreach $clock (sort by_clocks keys %clockn) {
    next if !defined($opt_B) && $clock =~ /rdbe(.)\/!dbe_(.)ps/ &&
        defined($clockn{"rdtc$1#dot2$2ps"}) ;
    next if !defined($opt_B) && $clock =~ /mcast_time\/(\w+)/ &&
        defined($clockn{"pps2dot/$1"}) ;
    $#val=-1;
    $#tim=-1;
    $avg=0;
    $count=0;
    for($j=0;$j<$clockn{$clock};$j++) {
	$val[$j]='$$$';
	$tim[$j]=$clockt{$clock}[$j];
	next unless $clockv{$clock}[$j]=~/^$float_pattern$/ ;
	next unless defined($opt_C) || abs($clockv{$clock}[$j])<1e1 ;
	$val[$j]=$clockv{$clock}[$j]*=1e6;
	$avg+=$val[$j];
	$count++;
    }
#
# it seems PGPLOT can't handle lots of significant digits
#
#    print "$avg $count\n";
    $avg=$avg/$count if $count != 0;
    if($avg > 500000) {
	$offset=1e6;
	$label="($clock)-1e6 (us)";
    } elsif($avg <-500000) {
	$offset=-1e6;
	$label="($clock)+1e6 (us)";
    } else {
	$offset=0;
	$label="$clock (us)";
    }
#    print "$offset $label\n";
    if($offset) {
#	print "apply offset $offset\n";
	for($j=0;$j<$clockn{$clock};$j++) {
	    next unless $val[$j]=~/^$float_pattern$/ ;
	    $val[$j]-=$offset;
	}
    }
    &vu(\@tim,\@val,$label);
}
if(@setcl) {
     &vu(\@setclt, \@setcl,"setcl offset (0.01 s)");
}
if(@recordt) {
    &vu(\@recordt, \@recordd,"Record delay (sec)");
    &vu(\@recordt, \@records,"Recording short (sec)");
    &vu(\@recordt, \@recordm,"Missing bytes");
}
foreach $tone (sort by_rdbe_tone keys %rpcaln) {
    $#amp=-1;
    $#tim=-1;
    $#phs=-1;
    for($j=0;$j<$rpcaln{$tone};$j++) {
	$amp[$j]=$rpcala{$tone}[$j];
	$tim[$j]=$rpcalt{$tone}[$j];
	$phs[$j]=$rpcalp{$tone}[$j];
    }
    if(!defined($opt_j)) {
	&vu(\@tim,\@amp,"$tone Amp");
	&vu(\@tim,\@phs,"$tone Phase");
    } else {
	die "Option -j is not supported for RDBE racks\n";
    }
}
for($i=1;$i<17;$i++) {
    for($k=0;$k<2;$k++) {
	if(defined($pcaln[$k][$i])) {
	    $#amp=-1;
	    $#tim=-1;
	    $#phs=-1;
	    for($j=0;$j<$pcaln[$k][$i];$j++) {
		$amp[$j]=$pcala[$k][$i][$j];
		$tim[$j]=$pcalt[$k][$i][$j];
		$phs[$j]=$pcalp[$k][$i][$j];
	    }
#
# fix up to use dual sideband tsys (MK IV) or opposite sideband (VLBA) for
# normalization if specific sideband data doesn't exist,
# -b forces same sideband, reject point if not there
#
	    $chan=$pcall[$k][$i];
	    if(!defined($opt_b)) {
		substr($chan,-1)="d";
		$chan=$pcall[$k][$i] if !defined($tsysn{$chan});
		if(!defined($tsysn{$chan}) && substr($hcan,-1) eq 'u') {
		    substr($chan,-1)="l";
		    $chan=$pcall[$k][$i] if !defined($tsysn{$chan});
		}
		if(!defined($tsysn{$chan}) && substr($chan,-1) eq 'l') {
		    substr($chan,-1)="u";
		    $chan=$pcall[$k][$i] if !defined($tsysn{$chan});
		}
	    }

	    if(!defined($opt_j)) {
		&vu(\@tim,\@amp,"$pcalf[$k][$i] Hz Amp $pcall[$k][$i]");
	    } elsif(defined($tsysn{$chan})) {
		($ptref,$pvref)= &nrml($k, $i, $chan);
		&vu($ptref,$pvref,"$pcalf[$k][$i] Hz Amp(Ts) $pcall[$k][$i]");
	    }
	    &vu(\@tim,\@phs,"$pcalf[$k][$i] Hz Phase $pcall[$k][$i]");
	}
    }
}

if($rdbe) {
    foreach $detect (sort by_rdbe_chan keys %tsysn) {
#    print "Detect $detect $tsysn{$detect}\n";
	$#amp=-1;
	$#tim=-1;
	for($j=0;$j<$tsysn{$detect};$j++) {
	    $amp[$j]=$tsysv{$detect}[$j];
	    $tim[$j]=$tsyst{$detect}[$j];
	}
	$label=sprintf("$string %s",$detect);
	&vu(\@tim,\@amp,$label);
    }
} elsif($dbbc3) {
    foreach $detect (sort by_dbbc3_chan keys %tsysn) {
#    print "Detect $detect $tsysn{$detect}\n";
	$#amp=-1;
	$#tim=-1;
	for($j=0;$j<$tsysn{$detect};$j++) {
	    $amp[$j]=$tsysv{$detect}[$j];
	    $tim[$j]=$tsyst{$detect}[$j];
	}
	$label=sprintf("$string %s",$detect);
	&vu(\@tim,\@amp,$label);
    }
} else {
    foreach $detect (sort keys %tsysn) {
#    print "Detect $detect $tsysn{$detect}\n";
	$#amp=-1;
	$#tim=-1;
	for($j=0;$j<$tsysn{$detect};$j++) {
	    $amp[$j]=$tsysv{$detect}[$j];
	    $tim[$j]=$tsyst{$detect}[$j];
	}
	$label=sprintf("$string %s",$detect);
	&vu(\@tim,\@amp,$label);
    }
}
foreach $detect (sort keys %tsysn1) {
#    print "Detect $detect $tsysn1{$detect}\n";
    $#amp=-1;
    $#tim=-1;
    for($j=0;$j<$tsysn1{$detect};$j++) {
	$amp[$j]=$tsysv1{$detect}[$j];
	$tim[$j]=$tsyst1{$detect}[$j];
    }
    $label=sprintf("Tsys1 %s",$detect);
    &vu(\@tim,\@amp,$label);
}	
foreach $detect (sort keys %tsysn2) {
#    print "Detect $detect $tsysn2{$detect}\n";
    $#amp=-1;
    $#tim=-1;
    for($j=0;$j<$tsysn2{$detect};$j++) {
	$amp[$j]=$tsysv2{$detect}[$j];
	$tim[$j]=$tsyst2{$detect}[$j];
    }
    $label=sprintf("Tsys2 %s",$detect);
    &vu(\@tim,\@amp,$label);
}	
foreach $detect (sort keys %tsysn3) {
#    print "Detect $detect $tsysn3{$detect}\n";
    $#amp=-1;
    $#tim=-1;
    for($j=0;$j<$tsysn3{$detect};$j++) {
	$amp[$j]=$tsysv3{$detect}[$j];
	$tim[$j]=$tsyst3{$detect}[$j];
    }
    $label=sprintf("Tsys3 %s",$detect);
    &vu(\@tim,\@amp,$label);
}	
foreach $chan (sort keys %rxcount) {
    $#amp=-1;
    $#tim=-1;
    for($j=0;$j<$rxcount{$chan};$j++) {
	$amp[$j]=$rx{$chan}[$j];
	$tim[$j]=$rxtime{$chan}[$j];
    }
    $units="";
    $units=" ($rxunits{$chan})" if $rxunits{$chan};
    $label="$rxcmd $chan$units";
#    print "$chan\n";
    &vu(\@tim,\@amp,$label);
}	
	
goto END_EXTRAS;

EXTRAS:

if(defined($opt_v)) {
    @tones=sort by_rdbe_tone keys %rpcaln;
    for ($i=0;$i<=$#tones;$i++) {
	$if_band=substr($tones[$i],0,2);
	for ($j=$i+1;$j<=$#tones;$j++) {
	    if(substr($tones[$j],0,2) ne $if_band) {
		last;
	    }
	    &phsr($tones[$i],$tones[$j]);
	}
    }
}
# $done is not redundant, it prevents re-plotting of differences when
# when multiple options are specified
if(defined($opt_1)) {
    for($i=1;$i<15;$i++) {
	for($m=0;$m<2;$m++) {
	    if(defined($pcaln[$m][$i])) {
		for ($j=$i+1;$j<15;$j++) {
		    for($n=0;$n<2;$n++) {
			if(defined($pcaln[$n][$j]) && !$done[$i][$j]) {
			    $done[$i][$j]=1;
			    &phs($i,$j,$m,$n);
			}
		    }
		}
	    }
	}
    }
}
if(defined($opt_a)) {
    for($i=1;$i<15;$i+=2) {
	for($m=0;$m<2;$m++) {
	    if(defined($pcaln[$m][$i])) {
		for ($j=$i+2;$j<15;$j+=2) {
		    for($n=0;$n<2;$n++) {
			if(defined($pcaln[$n][$j]) && !$done[$i][$j]) {
			    $done[$i][$j]=1;
			    &phs($i,$j,$m,$n);
			}
		    }
		}
	    }
	}
    }
    for($i=2;$i<15;$i+=2) {
	for($m=0;$m<2;$m++) {
	    if(defined($pcaln[$m][$i])) {
		for ($j=$i+2;$j<15;$j+=2) {
		    for($n=0;$n<2;$n++) {
			if(defined($pcaln[$n][$j]) && !$done[$i][$j]) {
			    $done[$i][$j]=1;
			    &phs($i,$j,$m,$n);
			}
		    }
		}
	    }
	}
    }
}
if(defined($opt_n)) {
    for($i=1;$i<9;$i++) {
	for($m=0;$m<2;$m++) {
	    if(defined($pcaln[$m][$i])) {
		for ($j=$i+1;$j<9;$j++) {
		    for($n=0;$n<2;$n++) {
			if(defined($pcaln[$n][$j]) && !$done[$i][$j]) {
			    $done[$i][$j]=1;
			    &phs($i,$j,$m,$n);
			}
		    }
		}
	    }
	}
    }
    for($i=9;$i<15;$i++) {
	for($m=0;$m<2;$m++) {
	    if(defined($pcaln[$m][$i])) {
		for ($j=$i+1;$j<15;$j++) {
		    for($n=0;$n<2;$n++) {
			if(defined($pcaln[$n][$j]) && !$done[$i][$j]) {
			    $done[$i][$j]=1;
			    &phs($i,$j,$m,$n);
			}
		    }
		}
	    }
	}
    }
}
if(defined($opt_w)) {
    for($i=1;$i<5;$i++) {
	for($m=0;$m<2;$m++) {
	    if(defined($pcaln[$m][$i])) {
		for ($j=$i+1;$j<5;$j++) {
		    for($n=0;$n<2;$n++) {
			if(defined($pcaln[$n][$j]) && !$done[$i][$j]) {
			    $done[$i][$j]=1;
			    &phs($i,$j,$m,$n);
			}
		    }
		}
	    }
	}
    }
    for($i=5;$i<9;$i++) {
	for($m=0;$m<2;$m++) {
	    if(defined($pcaln[$m][$i])) {
		for ($j=$i+1;$j<9;$j++) {
		    for($n=0;$n<2;$n++) {
			if(defined($pcaln[$n][$j]) && !$done[$i][$j]) {
			    $done[$i][$j]=1;
			    &phs($i,$j,$m,$n);
			}
		    }
		}
	    }
	}
    }
    for($i=9;$i<15;$i++) {
	for($m=0;$m<2;$m++) {
	    if(defined($pcaln[$m][$i])) {
		for ($j=$i+1;$j<15;$j++) {
		    for($n=0;$n<2;$n++) {
			if(defined($pcaln[$n][$j]) && !$done[$i][$j]) {
			    $done[$i][$j]=1;
			    &phs($i,$j,$m,$n);
			}
		    }
		}
	    }
	}
    }
}
if(defined($opt_1) || defined($opt_a) || defined($opt_n) || defined($opt_w)) {
    for($i=1;$i<15;$i++) {
	if(defined($pcaln[1][$i]) && defined($pcaln[0][$i])) {
	    $done[$i][$i]=1;
	    &phs($i,$i,0,1);
	}
    }
}

END_EXTRAS:
    $something=0;
    $exit_code=0;

if(!$first) {
    pgtbox("BCTNZYH",0.0,0,"",0.0,0);
    pglabel("Time","","");
    $something=1;
} elsif($page==1 && !defined($opt_p)){
    pgpanl(1,1);
    pgvport(0.15,.95,0,1);
    pgwindow(-0.5,16.5,0,39);
    pgtext(4,10,"$location Log Plots -- $refdate $save_file Page $page");
    pgtext(6,0,"NOTHING FOUND TO PLOT");
    $exit_code=-2;
}

if(defined($opt_p)) {
    $first = 1;
    for($i=1;$i<17;$i++) {
        for($k=0;$k<2;$k++) {
	    if(defined($pcaln[$k][$i])) {
		$#amp=-1;
		$#tim=-1;
		$#phs=-1;
		for($j=0;$j<$pcaln[$k][$i];$j++) {
		    $amp[$j]=$pcala[$k][$i][$j];
		    $tim[$j]=$pcalt[$k][$i][$j];
		    $phs[$j]=$pcalp[$k][$i][$j];
		}
#
# fix up to use dual sideband tsys (MK IV) or opposite sideband (VLBA) for
# normalization if specific sideband data doesn't exist,
# -b forces same sideband, reject point if not there
#
		$chan=$pcall[$k][$i];
		if(!defined($opt_b)) {
		    substr($chan,-1)="d";
		    $chan=$pcall[$k][$i] if !defined($tsysn{$chan});
			if(!defined($tsysn{$chan}) && substr($chan,-1) eq 'u') {
			    substr($chan,-1)="l";
			    $chan=$pcall[$k][$i] if !defined($tsysn{$chan});
			}
			if(!defined($tsysn{$chan}) && substr($chan,-1) eq 'l') {
			    substr($chan,-1)="u";
			    $chan=$pcall[$k][$i] if !defined($tsysn{$chan});
			}
		}
		if(!defined($opt_j)) {
		    if($first == 1 && $panel !=0) {
			pgask(-1);
			pgpanl(1,8);
			pgpage();
			$panel=0;
		    }
		    $label=sprintf("$pcalf[$k][$i] Hz Amp $pcall[$k][$i]");
		    &vup(\@phs,\@amp,$label);
		} elsif (defined($tsysn{$chan})) {
		    ($ptref,$pvref)= &nrml($k, $i, $chan);
		    $#tima=-1;
		    $#vala=-1;
		    for($j=0;$j<=$#$ptref;$j++) {
			$tima[$j]=$$ptref[$j];
			$vala[$j]=$$pvref[$j];
		    }
		    ($ptref,$pvref,$atref,$avref)=&match(\@tim,\@phs,\@tima,\@vala);
		    $label=sprintf("$pcalf[$k][$i] Hz Amp(Ts) $pcall[$k][$i]");
		    &vup($pvref,$avref,$label);
		}
	    }
	}
    }
    foreach $tone (sort by_rdbe_tone keys %rpcaln) {
	$#amp=-1;
	$#phs=-1;
	for($j=0;$j<$rpcaln{$tone};$j++) {
	    $amp[$j]=$rpcala{$tone}[$j];
	    $phs[$j]=$rpcalp{$tone}[$j];
	}
	if($first == 1 && $panel !=0) {
	    pgask(-1);
	    pgpanl(1,8);
	    pgpage();
	    $panel=0;
	}
	if(!defined($opt_j)) {
	    &vup(\@phs,\@amp,"$tone Amp");
	} else {
	    die "Option -j is not supported for RDBE racks\n";
	}
    }
    if(!$first) {
	pgtbox("BCTN",45.0,0,"",0.0,0);
	pglabel("Phase","","");
    } elsif($page==1 && !$something) {
	pgpanl(1,1);
	pgvport(0.15,.95,0,1);
	pgwindow(-0.5,16.5,0,39);
	pgtext(4,10,"$location Log Plots -- $refdate $save_file Page $page");
	pgtext(6,0,"NOTHING FOUND TO PLOT");
        $exit_code=-2;
    }
}

pgpanl(1,8);
pgvport(0.15,.95,0,1);
pgwindow(-0.5,16.5,0,39);
pgtext(7.25,15,"Last Page");

pgend();
exit $exit_code;
